# _*_ coding: utf-8 _*_
# .@FileName:rename
# .@Data....:2025-02-06 : 16 : 34
# .@Aurhor..:LiuJingYu
"""
launch:
   import maya.cmds as cmds
   FileName.main() 
"""
from __future__ import unicode_literals, print_function
import maya.cmds as mc
import re
from contextlib import contextmanager


# def _selection_list_uids():
#     sel = mc.ls(sl=True, l=True)
#     nodes = [n for i in sel for o in ([i], mc.listRelatives(i, ad=True, pa=True)) for n in o]
#     uids = mc.ls(nodes, uid=True)
#     return uids
#
# def add_name_prefix(prefix):
#
#     for uid in _selection_list_uids():
#         n = mc.ls(uid)[0]
#         mc.rename(n, prefix + n.split("|")[-1])
#
# def search_replace(search, replace):
#     for uid in _selection_list_uids():
#         n = mc.ls(uid)[0]
#         sn = n.split("|")[-1]
#         mc.rename(search, replace)
#
# def regex_search_replace_name(search, replace):
#     for uid in _selection_list_uids():
#         re_o = re.compile(search)
#         n = mc.ls(uid)[0]
#         sn = n.split("|")[-1]
#
#         mc.rename(n, re_o.sub(replace, sn))



@contextmanager
def _undo_chunk(name="RenameOperation"):
    """
    合并操作到单个undo步骤。
    在Maya中，使用这个上下文管理器可以将一系列操作合并为一个撤销步骤，
    方便用户在需要时一次性撤销整个重命名操作。

    参数:
    name (str): 撤销块的名称，默认为 "RenameOperation"
    """
    try:
        # 打开一个撤销块，开始记录操作
        mc.undoInfo(openChunk=True, chunkName=name)
        # 暂停上下文管理器，允许外部代码执行操作
        yield
    finally:
        # 关闭撤销块，结束操作记录
        mc.undoInfo(closeChunk=True)


def _get_hierarchy_nodes():
    """
    优化后的节点收集逻辑。
    此函数用于收集当前选中对象及其所有子对象，并对节点进行去重和排序。

    返回:
    list: 包含所有选中对象及其子对象的列表，按层级倒序排序（从叶子到根）
    """
    # 获取当前选中的对象，使用长名称
    selection = mc.ls(selection=True, long=True) or []
    if not selection:
        # 如果没有选中任何对象，发出警告并返回空列表
        mc.warning("没有选中任何对象")
        return []

    # 使用集合去重并收集所有节点
    all_nodes = set()
    for node in selection:
        # 获取当前节点的所有后代节点
        descendants = mc.listRelatives(node, allDescendents=True, fullPath=True) or []
        # 将当前节点及其后代节点添加到集合中
        all_nodes.update([node] + descendants)

    # 按层级倒序排序（从叶子到根），通过计算节点名称中 "|" 的数量来确定层级
    return sorted(all_nodes, key=lambda x: x.count("|"), reverse=True)


def _safe_rename(old_name, new_name):
    """
    安全重命名（带冲突检查）。
    在重命名节点之前，会检查新名称是否已经存在，避免重命名冲突。

    参数:
    old_name (str): 节点的旧名称
    new_name (str): 节点的新名称

    返回:
    str: 重命名后的节点名称

    异常:
    ValueError: 如果新名称已经存在，则抛出此异常
    """
    if mc.objExists(new_name):
        # 如果新名称已经存在，抛出异常
        raise ValueError(f"名称已存在: {new_name}")
    # 执行重命名操作并返回新名称
    return mc.rename(old_name, new_name)


def add_name_prefix(prefix):
    """
    添加名称前缀。
    为当前选中对象及其子对象的名称添加指定的前缀。

    参数:
    prefix (str): 要添加的前缀
    """
    # 使用 _undo_chunk 上下文管理器将重命名操作合并为一个撤销步骤
    with _undo_chunk("AddPrefix"):
        # 遍历所有选中对象及其子对象
        for node in _get_hierarchy_nodes():
            # 获取节点的短名称（去掉路径信息）
            base_name = node.split("|")[-1]
            try:
                # 执行安全重命名操作，添加前缀
                _safe_rename(node, f"{prefix}{base_name}")
            except Exception as e:
                # 如果重命名失败，发出警告并打印错误信息
                mc.warning(f"重命名失败 {node}: {str(e)}")


def search_replace_name(search_str, replace_str):
    """
    字符串搜索替换。
    在当前选中对象及其子对象的名称中搜索指定字符串，并将其替换为新字符串。

    参数:
    search_str (str): 要搜索的字符串
    replace_str (str): 用于替换的字符串
    """
    # 使用 _undo_chunk 上下文管理器将重命名操作合并为一个撤销步骤
    with _undo_chunk("SearchReplace"):
        # 遍历所有选中对象及其子对象
        for node in _get_hierarchy_nodes():
            # 获取节点的短名称（去掉路径信息）
            base_name = node.split("|")[-1]
            # 执行字符串替换操作
            new_name = base_name.replace(search_str, replace_str)
            if new_name != base_name:
                try:
                    # 如果名称发生了变化，执行安全重命名操作
                    _safe_rename(node, new_name)
                except Exception as e:
                    # 如果重命名失败，发出警告并打印错误信息
                    mc.warning(f"替换失败 {node}: {str(e)}")


def regex_search_replace_name(pattern, replacement):
    """
    正则表达式替换。
    使用正则表达式在当前选中对象及其子对象的名称中进行搜索和替换。

    参数:
    pattern (str): 正则表达式模式
    replacement (str): 用于替换的字符串

    异常:
    ValueError: 如果正则表达式无效，则抛出此异常
    """
    try:
        # 编译正则表达式
        regex = re.compile(pattern)
    except re.error as e:
        # 如果正则表达式无效，抛出异常并显示错误信息
        raise ValueError(f"无效正则表达式: {pattern}") from e

    # 使用 _undo_chunk 上下文管理器将重命名操作合并为一个撤销步骤
    with _undo_chunk("RegexReplace"):
        # 遍历所有选中对象及其子对象
        for node in _get_hierarchy_nodes():
            # 获取节点的短名称（去掉路径信息）
            base_name = node.split("|")[-1]
            # 使用正则表达式进行替换操作
            new_name = regex.sub(replacement, base_name)
            if new_name != base_name:
                try:
                    # 如果名称发生了变化，执行安全重命名操作
                    _safe_rename(node, new_name)
                except Exception as e:
                    # 如果重命名失败，发出警告并打印错误信息
                    mc.warning(f"正则替换失败 {node}: {str(e)}")